package com.pat.jrgwt.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;

public abstract class BaseTableView extends Composite
{

    /**
     * Inicates whether data was loaded
     */
    protected boolean m_isLoaded = false;
    
    /**
     * Where all data is displayed
     */
    protected FlexTable m_table = new FlexTable();
    
    /**
     * To display over table
     */
    protected Label m_lbl = new Label("Base table view");
    
    /**
     * All data goes here (rows x columns)
     */
    protected double[][] m_arCellValues = null;
    
    /**
     * To display table header (for columns)
     */
    protected String[] m_arHeaders = null;
    
    /**
     * Rows labels
     */
    protected String[] m_arRowLabels = null;
    
    protected NumberFormat m_valueFormatter = null;
    
//    private static class SingletonHolder
//    {
//        private static final BaseTableView instance = new BaseTableView();
//    }
//    
//    /**
//     * Returns the only class instance
//     */
//    public static BaseTableView getInstance()
//    {
//        return SingletonHolder.instance;
//    }
    
    public BaseTableView()
    {
        GWT.log("BaseTableView contscructor...");
        m_valueFormatter = NumberFormat.getFormat("###,##0.00");
        
        // Sets data to display
        setHeaders();
        setRowLabels();
        
        // Resize data array
//        m_arCellValues = new double[getDataRows()][getDataColumns()];

        
        VerticalPanel p = new VerticalPanel();
        m_lbl.setStylePrimaryName("cellBold");
        p.add(m_lbl);
        
        m_table.setBorderWidth(1);
        p.add(m_table);
        
        initWidget(p);
    }
    
    /**
     * Every class should implement recalculation
     */
    public abstract void recalculate();
    
    /**
     * Sets columns headers
     */
    protected abstract void setHeaders();
    
    /**
     * Sets labels for rows
     */
    protected abstract void setRowLabels();
    
    /**
     * Assigns new header for table. All data will be lost
     * @param arHeaders - headers for columns
     * @param arRowLabels labels for rows
     */
    public void setTableHeaders(String[] arHeaders, String[] arRowLabels)
    {
        m_arHeaders = arHeaders;
        m_arRowLabels = arRowLabels;
        
        // Resize data array
        m_arCellValues = new double[m_arRowLabels.length][m_arHeaders.length];
    }

    /**
     * Recalculates and re-draws whole tables (headers and data)
     */
    public void redrawFull()
    {
        recalculate();
        drawTable();
        drawValues();
    }
    
    /**
     * Displays whole table on screen - headers and data cells
     */
    public void drawTable()
    {
        drawHeaders();
        
        drawValues();
    }
    
    protected void drawHeaders()
    {
//        GWT.log("Is m_arHeaders null? " + (m_arHeaders == null ? "yes" : "no"));
//        GWT.log("Is m_arRowLabels null? " + (m_arRowLabels == null ? "yes" : "no"));
        for( int i = 0; i < m_arHeaders.length; i++ )
        {
            m_table.setText(0, i + 1, m_arHeaders[i]);
            m_table.getCellFormatter().setStylePrimaryName(0, i + 1, "jrCellHeader");
        }
        
        for( int i = 0; i < m_arRowLabels.length; i++ )
        {
            m_table.setText(i + 1, 0, m_arRowLabels[i]);
            m_table.getCellFormatter().setStylePrimaryName(i + 1, 0, "jrRowLabel");
        }
    }

    /**
     * Redraws values on screen
     */
    public void drawValues()
    {
//        GWT.log("Is m_valueFormatter null? " + (m_valueFormatter == null ? "yes" : "no"));
        for( int column = 1; column <= m_arHeaders.length; column++ )
        {
            for( int row = 1; row <= m_arRowLabels.length; row++ )
            {
                m_table.setText(row, column, m_valueFormatter.format(m_arCellValues[row -1][column - 1]));
            }
        }
    }
    
    public boolean isDataLoaded()
    {
        return m_isLoaded;
    }
    
    // Cells and values access

    /**
     * Returns value for specified cell or Dobule.MIN_VALUE if out of bounds
     * 
     * @param row 1-based index for row (row x column)
     * @param column 1-base index for column (row x column)
     */
    public double getSafe(int row, int column)
    {
        if( (row > 0) && (row <= getDataRows()) && (column > 0) && (column <= getDataColumns()) )
        {
            return m_arCellValues[row - 1][column - 1];
        }
        return Double.MIN_VALUE;
    }
    
    public double getSafe(int row, String header)
    {
        double v = Double.NaN;
        int pos = getHeaderPosition(header);
        
        if( pos != -1 )
        {
            
        }
        
        return v;
    }
    
    /**
     * Returns postion of header by its name or -1
     * @param name
     * @return
     */
    public int getHeaderPosition(String name)
    {
        name = name.toUpperCase();
        for( int i = 0; (m_arHeaders != null) && (i < m_arHeaders.length); i++ )
        {
            if( m_arHeaders[i].toUpperCase() == name )
            {
                return i;
            }
        }
        return -1;
    }
    
    /**
     * Sets value to data array or throws {@link ArrayIndexOutOfBoundsException}. Indexes ar 1-based 
     * @param row
     * @param column
     * @param value
     */
    public void setAt(int row, int column, double value)
    {
        if( (row < 1) || (row > getDataRows()) || (column < 1) || (column > getDataColumns()) )
        {
            throw new ArrayIndexOutOfBoundsException("Set data should be in [" + getDataRows() + "x" + getDataColumns() + "]!");
        }
        
        m_arCellValues[row - 1][column - 1] = value;
    }
    
    /**
     * Returns number of data rows (safe)
     * @return
     */
    public int getDataRows()
    {
        return m_arCellValues == null ? 0 : m_arCellValues.length;
    }
 
    /**
     * Returns number of data columns
     * @return
     */
    public int getDataColumns()
    {
        return m_arCellValues == null ? 0 : m_arCellValues[0].length;
    }

    public void setLabel(String lbl)
    {
        m_lbl.setText(lbl);
    }
    
}
